#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include "driver_si7006.h"

#define CNAME "mysi7006"

struct i2c_client *si7006_client;
struct cdev *cdev;
struct class *cls;
struct device *dev;
int major = 0;
int minor = 0;

int i2c_read_reg(u8 reg)
{
    int ret;
    u16 data;
    u8 r_buf[] = {reg};
    // 1.封装消息
    struct i2c_msg r_msg[2] = {
        [0] = {
            .addr = si7006_client->addr,
            .flags = 0,
            .len = 1,
            .buf = r_buf,
        },
        [1] = {
            .addr = si7006_client->addr,
            .flags = 1,
            .len = 2,
            .buf = (u8 *)&data,
        },
    };
    // 2.发送消息
    ret = i2c_transfer(si7006_client->adapter, r_msg, 2);
    if (ret != 2)
    {
        pr_err("i2c_transfer read error\n");
        return -EAGAIN;
    }
    data = data << 8 | data >> 8; // 调换高低字节
    return data;
}

int si7006_open(struct inode *inode, struct file *file)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);

    return 0;
}
ssize_t si7006_read(struct file *file, char __user *user, size_t size, loff_t *loffs)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return size;
}
ssize_t si7006_write(struct file *file, const char __user *user, size_t size, loff_t *loffs)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return size;
}
int si7006_close(struct inode *inode, struct file *file)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int data;
    int ret;
    switch (cmd)
    {
    case GET_TMP:
        data = i2c_read_reg(TMP_ADDR);
        if (data < 0)
        {
            printk(KERN_ERR "i2c_read_reg temp error!\n");
            return -data;
        }
        break;
    case GET_HUM:
        data = i2c_read_reg(HUM_ADDR);
        if (data < 0)
        {
            printk(KERN_ERR "i2c_read_reg hum error!\n");
            return -data;
        }
        break;

    default:
        break;
    }

    ret = copy_to_user((void *)arg, &data, _IOC_SIZE(cmd));
    if (ret)
    {
        printk(KERN_ERR "copy_to_user error!\n");
        return -EIO;
    }

    return 0;
}

const struct file_operations fops = {
    .open = si7006_open,
    .read = si7006_read,
    .write = si7006_write,
    .release = si7006_close,
    .unlocked_ioctl = si7006_ioctl,
};

int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret;
    dev_t devno;

    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);
    si7006_client = client;
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk(KERN_ERR "cdev_alloc error!\n");
        ret = -ENOMEM;
        goto err1;
    }

    cdev_init(cdev, &fops);

    ret = alloc_chrdev_region(&devno, 0, 1, CNAME);
    if (ret)
    {
        printk(KERN_ERR "alloc_chrdev_region error");
        goto err2;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);

    ret = cdev_add(cdev, MKDEV(major, minor), 1);
    if (ret)
    {
        printk(KERN_ERR "cdev_add error");
        goto err3;
    }

    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls))
    {
        printk(KERN_ERR "class_create error");
        ret = PTR_ERR(cls);
        goto err4;
    }

    dev = device_create(cls, NULL, MKDEV(major, minor), NULL, CNAME);
    if (IS_ERR(dev))
    {
        printk(KERN_ERR "device_create error");
        ret = PTR_ERR(dev);
        goto err5;
    }

    return 0;

err5:
    class_destroy(cls);

err4:
    cdev_del(cdev);

err3:
    unregister_chrdev_region(MKDEV(major, minor), 1);

err2:
    kfree(cdev);

err1:
    return ret;
}
int si7006_remove(struct i2c_client *client)
{
    printk("%s,%s,%d\n", __FILE__, __func__, __LINE__);

    device_destroy(cls, MKDEV(major, minor));
    class_destroy(cls);
    cdev_del(cdev);
    unregister_chrdev_region(MKDEV(major, minor), 1);
    kfree(cdev);
    return 0;
}

struct of_device_id oftable[] = {
    {
        .compatible = "hqyj,si7006",
    },
    {}};

/* platform驱动结构体 */
struct i2c_driver si7006_driver = {
    .probe = si7006_probe,
    .remove = si7006_remove,
    .driver = {
        .name = "si7006",
        .of_match_table = oftable,
    }};

module_i2c_driver(si7006_driver);
MODULE_LICENSE("GPL");
